<?php
/**
 * Pronounceable word class file.
 *
 * Infectamic: browser game (http://infectamic.goldzoneweb.info)
 * Copyright 2011, Loic Chardonnet
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 */

/**
 * Class for pronounceable word.
 */
class PronounceableWord {
    public $minLength = MINIMUM_NAME_LENGTH;
    public $maxLength = MAXIMUM_NAME_LENGTH;
    public $finalLength = 8;

    protected $word = '';
    protected $currentLength = 0;
    protected $letterTypes = array(
        'voyels' => 'aeiouy',
        'consonants' => 'bcdfghjklmnpqrstvwxz',
    );
    protected $letters = array(
        'a' => 'abcdefhiklmnorstuvwxyz',
        'b' => 'abeiloruy',
        'c' => 'acehikloruy',
        'd' => 'aegilmnorsuy',
        'e' => 'abcdegiklmnoprstvwxy',
        'f' => 'aeilry',
        'g' => 'aeghiloruwy',
        'h' => 'aeiouy',
        'i' => 'acdefglmnopqrstvxz',
        'j' => 'aeou',
        'k' => 'aeiruy',
        'l' => 'abcdefghijklmnoprstuvwxyz',
        'm' => 'abeimosuy',
        'n' => 'adeilnorsty',
        'o' => 'abcdefghiklmnoprstuwy',
        'p' => 'aehilort',
        'q' => 'u',
        'r' => 'acdegiklmnoprsuvy',
        's' => 'acehilopstuy',
        't' => 'aehinortuwy',
        'u' => 'abcdefgklmnprst',
        'v' => 'aeio',
        'w' => 'aehinory',
        'x' => 'aiu',
        'y' => 'acdelnorstz',
        'z' => 'aioyz',
    );

    public function __construct($minLength = MINIMUM_NAME_LENGTH, $maxLength = MAXIMUM_NAME_LENGTH) {
        $this->minLength = $minLength;
        $this->maxLength = $maxLength;

        $this->finalLength = rand($this->minLength, $this->maxLength);
    }

    /**
     * Generates a random pronounceable word.
     *
     * @return string The generated pronounceable word
     */
    public function generate() {
        for ($letter = 0; $letter < $this->finalLength; $letter++) {
            $this->addToWord($this->pickNextLetter());
        }

        return $this->word;
    }

    /**
     * Adds the given string the the word.
     *
     * @param string $string The string to add
     */
    protected function addToWord($string) {
        $this->word .= $string;
        $this->currentLength = strlen($this->word);
    }

    /**
     * Picks randomly the next letter.
     *
     * @return string The picked letter
     */
    protected function pickNextLetter() {
        $pickedLetter = '';
        if (true === empty($this->word)) {
            $pickedLetter = $this->pickFirstLetter();
        } else {
            $pickedLetter = $this->pickLetterFromGivenChoice($this->choiceOfNonConsecutiveLettersLinkedToLast());
        }

        return $pickedLetter;
    }

    /**
     * Picks randomly the first letter.
     *
     * @return string The picked letter
     */
    protected function pickFirstLetter() {
        $pickedLetter = array_rand($this->letters);

        return $pickedLetter;
    }

    /**
     * Picks randomly a letter from the given choice.
     *
     * @param string $choiceOfLetters The given choice of letters
     * @return string The picked letter
     */
    protected function pickLetterFromGivenChoice($choiceOfLetters) {
        $minLetterIndex = 0;
        $maxLetterIndex = strlen($choiceOfLetters) - 1;
        $pickedLetterIndex = rand($minLetterIndex, $maxLetterIndex);

        $pickedLetter = $choiceOfLetters[$pickedLetterIndex];

        return $pickedLetter;
    }

    /**
     * Returns a choice of letters linked to the last.
     *
     * @return string The choice of letters
     */
    protected function choiceOfLettersLinkedToLast() {
        $lastLetterIndex = $this->currentLength - 1;
        $lastLetter = $this->word[$lastLetterIndex];

        $choiceOfLetters = $this->letters[$lastLetter];

        return $choiceOfLetters;
    }

    /**
     * Returns a choice of letters linked to the last.
     *
     * @return string The next letters
     */
    protected function choiceOfNonConsecutiveLettersLinkedToLast() {
        $choiceOfLetters = $this->choiceOfLettersLinkedToLast();

        if (true === $this->isConsecutiveTypes()) {
            $choiceOfLetters = $this->removeLastTypeFromChoice($choiceOfLetters);
        }

        return $choiceOfLetters;
    }

    /**
     * Removes the given type of letter from the given choice of letters.
     *
     * @param string $type The type of letters to remove
     * @param string $choiceOfLetters The choice of letters
     * @return string The choice of letters without the type
     */
    protected function removeLastTypeFromChoice($choiceOfLetters) {
        $lastLetterIndex = $this->currentLength - 1;
        $lastLetter = $this->word[$lastLetterIndex];
        $lastType = $this->getLetterType($lastLetter);

        $letterIndexLimit = strlen($this->letterTypes[$lastType]);
        for ($letterIndex = 0; $letterIndex < $letterIndexLimit; $letterIndex++) {
            $choiceOfLetters = str_replace($this->letterTypes[$lastType][$letterIndex], '', $choiceOfLetters);
        }

        return $choiceOfLetters;
    }

    /**
     * Checks if the last consecutive letters have the same type.
     *
     * @return boolean True if theyr have the same type, or else false
     */
    protected function isConsecutiveTypes() {
        $isConsecutiveType = false;
        if ($this->nbOfConsecutiveTypes() >= LIMIT_OF_CONSECUTIVE_TYPES) {
            $isConsecutiveType = true;
        }

        return $isConsecutiveType;
    }

    /**
     * Counts the number of consecutive types.
     */
    protected function nbOfConsecutiveTypes() {
        $letterIndex = $this->currentLength - 1;
        $letter = $this->word[$letterIndex];
        $type = $this->getLetterType($letter);
        $nbOfConsecutiveTypes = 1;

        for ($letterIndex = $this->currentLength - 2; $letterIndex >= 0; $letterIndex--) {
            $letter = $this->word[$letterIndex];
            if ($this->getLetterType($letter) !== $type) {
                break;
            }
            $nbOfConsecutiveTypes++;
        }

        return $nbOfConsecutiveTypes;
    }

    /**
     * Gets the type of the given letter.
     *
     * @param string $letter The given letter
     * @return string The type of the letter
     */
    protected function getLetterType($letter) {
        $type = '';
        foreach ($this->letterTypes as $letterType => $letters) {
            if (false !== strpos($letters, $letter)) {
                $type = $letterType;
                break;
            }
        }

        return $type;
    }
}
